﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using SharpGL.SceneGraph;
using SharpGL;
using ProjetoKinect.Model;
using ProjetoKinect.Model3d;
using ProjetoKinect.openGL;
using SharpGL.Serialization;
using SharpGL.SceneGraph.Core;
using Microsoft.Kinect;
using ProjetoKinect.enums;
using ProjetoKinect;
using ProjetoKinect.Vieww3D;
using HelloLeapMotion;
using ProjetoKinect.LeapMotionGestures;
using LeapMotionMouse;

namespace HiSharpGL
{

    public partial class OpenGLWindow : Window
    {
        private static double INITIAL_X = 0;
        private static double INITIAL_Y = 0;
        private static double INITIAL_Z = 40;

        // rotation angle increment 
        public static int ANGLE_COUNTER = 3;

        private MyOpenGL myOpenGL;
        private List<TriangleObject> ascTriangleObjectList;

        private List<SharpGL.SceneGraph.Primitives.Polygon> polygons = new List<SharpGL.SceneGraph.Primitives.Polygon>();

        private float scale = 1.0f;
        private float angleY = 60.0f;
        private float angleX = 0.0f;

        private KinectController kinectController;
        private Boolean hasRemoved = false;
        private BodyControl bodyControl = new BodyControl();

        MyPoint3D cameraPosition = new MyPoint3D(INITIAL_X, INITIAL_Y, INITIAL_Z);
        private LeapMotionController leapMotionController = new LeapMotionController();

        private bool rotateXEnabled { get; set; }
        private bool rotateYEnabled { get; set; }
        private bool zoomEnabled { get; set; }

        public OpenGLWindow()
        {
            InitializeComponent();
            myOpenGL = new MyOpenGL();
            ParseObj parseObj1 = new ParseObj(@"C:\Imagens\blender\cubo.obj");
            ParseObj parseObj2 = new ParseObj(@"C:\Imagens\blender\osso2.obj");
            ParseObj parseObj3 = new ParseObj(@"C:\Imagens\blender\pele.obj");
            
            // ParseObj parseObj2 = null;
            // ParseObj parseObj3 = null;


            MyPoint3D boundBoxPoint = getBoundBox(parseObj1, parseObj2, parseObj3);
            ascTriangleObjectList = parseObj1.getTriangleObjects(Colors.Red, boundBoxPoint);
            ascTriangleObjectList.AddRange(parseObj2.getTriangleObjects(Colors.Orange, boundBoxPoint));
            ascTriangleObjectList.AddRange(parseObj3.getTriangleObjects(Colors.Blue, boundBoxPoint));

            foreach (TriangleObject triangleObject in ascTriangleObjectList)
            {
                triangleObject.getTriangleList().Sort(new MyTriangleAscComparer());
            }

           // new ConfigTransparentOBJ(ascTriangleObjectList, scale, angleX, angleY).Show();
            WindowState = WindowState.Maximized;

        }

        private MyPoint3D getBoundBox(ParseObj parseObj1, ParseObj parseObj2, ParseObj parseObj3)
        {
            float lessX = parseObj1.lessX;
            float lessY = parseObj1.lessY;
            float lessZ = parseObj1.lessZ;
            float biggerX = parseObj1.biggerX;
            float biggerY = parseObj1.biggerY;
            float biggerZ = parseObj1.biggerZ;
            if (parseObj2 != null)
            {
                if (parseObj2.lessX < lessX)
                {
                    lessX = parseObj2.lessX;
                }
                if (parseObj2.lessY < lessY)
                {
                    lessY = parseObj2.lessY;
                }
                if (parseObj2.lessZ < lessZ)
                {
                    lessZ = parseObj2.lessZ;
                }

                if (parseObj2.biggerX > biggerX)
                {
                    biggerX = parseObj2.biggerX;
                }
                if (parseObj2.biggerY > biggerY)
                {
                    biggerY = parseObj2.biggerY;
                }
                if (parseObj2.biggerZ > biggerZ)
                {
                    biggerZ = parseObj2.biggerZ;
                }
            }
            if (parseObj3 != null)
            {
                if (parseObj3.lessX < lessX)
                {
                    lessX = parseObj3.lessX;
                }
                if (parseObj3.lessY < lessY)
                {
                    lessY = parseObj3.lessY;
                }
                if (parseObj3.lessZ < lessZ)
                {
                    lessZ = parseObj3.lessZ;
                }

                if (parseObj3.biggerX > biggerX)
                {
                    biggerX = parseObj3.biggerX;
                }
                if (parseObj3.biggerY > biggerY)
                {
                    biggerY = parseObj3.biggerY;
                }
                if (parseObj3.biggerZ > biggerZ)
                {
                    biggerZ = parseObj3.biggerZ;
                }
            }
            float medX = (biggerX + lessX) / 2;
            float medY = (biggerY + lessY) / 2;
            float medZ = (biggerZ + lessZ) / 2;
            return new MyPoint3D(medX, medY, medZ);
        }

        public OpenGLWindow(List<TriangleObject> triangleObjectList, float scale, float angleX, float angleY)
        {
            InitializeComponent();
            myOpenGL = new MyOpenGL();

            this.ascTriangleObjectList = triangleObjectList;
            this.scale = scale;
            this.angleX = angleX;
            this.angleX = angleX;
            WindowState = WindowState.Maximized;

        }

        private void WindowLoaded(object sender, RoutedEventArgs e)
        {

            kinectController = new KinectController();
            kinectController.findKinectSensor();
            kinectController.enableSkeletonStream(this.SensorSkeletonFrameReady);

            OpenGL gl = openGLControl.OpenGL;
            myOpenGL.initialized(gl);
            openGLControl.OpenGLDraw += openGLControl_OpenGLDraw;
            openGLControl.OpenGLDraw -= openGLControl_OpenGLDraw;
            /*
            OpenGL glAxis = openGLControlAxis.OpenGL;
            myOpenGL.initialized(glAxis);
            openGLControlAxis.OpenGLDraw += openGLControl_OpenGLDrawAxis; 
            openGLControlAxis.OpenGLDraw -= openGLControl_OpenGLDrawAxis;
            */
        }

        private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {

            if (!hasRemoved)
            {
                Skeleton[] skeletons = kinectController.getSkeletons(e);
                foreach (Skeleton skel in skeletons)
                {
                    if (skel.TrackingState == SkeletonTrackingState.Tracked)
                    {
                        controlImage(skel);
                    }
                }
            }
        }

        private void controlImage(Skeleton skeleton)
        {
            selectedFunction.Text = "_";
            bodyControl.setSkeletonJoints(skeleton);

            MyJoint leftHandMyJoint = bodyControl.getMyJointByJointType(JointType.HandLeft);
            MyJoint rightHandMyJoint = bodyControl.getMyJointByJointType(JointType.HandRight);

            zoomWithGesture(bodyControl);
            rotateWithGesture(bodyControl);

            poseDetection();
      

            openGLControl.OpenGLDraw += openGLControl_OpenGLDraw;
            openGLControl.OpenGLDraw -= openGLControl_OpenGLDraw;


        }

        private void poseDetection()
        {
            if (bodyControl.isSurrenderPose(null))
            {
                this.selectedFunction.Text = bodyControl.getPoseMessage();
                if (bodyControl.getSecCount() >= 2)
                {
                    exit();
                }
            }
            else if (bodyControl.isTwoHandsFrontPosition(null))
            {
                this.selectedFunction.Text = bodyControl.getPoseMessage();
                if (bodyControl.getSecCount() >=2)
                {
                    goToConfigScreen();
                }
            }
            else
            {
                bodyControl.setPose(Pose.NONE);
            }
        }
        public void goToConfigScreen() {
            if (!hasRemoved)
            {
                openGLControl.OpenGLDraw -= openGLControl_OpenGLDraw;
                hasRemoved = true;
                this.kinectController.stopFullSensor();
                new ConfigTransparentOBJ(ascTriangleObjectList, scale, angleX, angleY).Show();
                this.Close();
            }
        
        }
       

        private void exit()
        {
            if (!hasRemoved)
            {
                hasRemoved = true;
                openGLControl.OpenGLDraw -= openGLControl_OpenGLDraw;
                this.kinectController.stopFullSensor();
               // LeapMotionController.getInstance().AddListener(MouseListener.getInstance());
                new ActivitySelector().Show();
                this.Close();
            }
        }

        private void exitWithSecurity()
        {
            Dispatcher.BeginInvoke(new System.Threading.ThreadStart(delegate
                  {
                      exit();
                  }));
        }

        private void zoomWithGesture(BodyControl bodyControl)
        {
            float zoomCounter = 0.03f;

            //zoom left hand upside
            if (bodyControl.areTwoHandsUp())
            {
                selectedFunction.Text = "Zoom";
                if (bodyControl.areTwoHandsGetClose())
                {
                    scale -= zoomCounter;
                }
                else if (bodyControl.areTwoHandGetAway())
                {
                    scale += zoomCounter;
                }
            }
        }

        private void rotateWithGesture(BodyControl bodyControl)
        {

            if (bodyControl.getLeftHandLocation().Equals(HandLocation.LD))
            {
                selectedFunction.Text = "Rotação X";
                if (bodyControl.isJoinDistantFromJointBase(JointType.HandRight, JointType.HipRight))
                {
                    if (bodyControl.getJointMovimentStatus(JointType.HandRight, Axis.AXIS_Y, 3).Equals(MovimentStatus.DECREASING))
                    {
                        angleX -= ANGLE_COUNTER;
                    }
                    else if (bodyControl.getJointMovimentStatus(JointType.HandRight, Axis.AXIS_Y, 3).Equals(MovimentStatus.INCREASING))
                    {
                        angleX += ANGLE_COUNTER;
                    }

                }
            }
            else if (bodyControl.getLeftHandLocation().Equals(HandLocation.LU) && !bodyControl.getRightHandLocation().Equals(HandLocation.RU))
            {
                selectedFunction.Text = "Rotação Y";
                if (bodyControl.isJoinDistantFromJointBase(JointType.HandRight, JointType.HipRight))
                {

                    if (bodyControl.getJointMovimentStatus(JointType.HandRight, Axis.AXIS_X, 3).Equals(MovimentStatus.DECREASING))
                    {
                        angleY += ANGLE_COUNTER;
                    }
                    else if (bodyControl.getJointMovimentStatus(JointType.HandRight, Axis.AXIS_X, 3).Equals(MovimentStatus.INCREASING))
                    {
                        angleY -= ANGLE_COUNTER;
                    }

                }
                
                
            }

        }

        private void openGLControl_OpenGLDraw(object sender, OpenGLEventArgs args)
        {
            leapMotionManipulation();

            OpenGL gl = openGLControl.OpenGL;

            //  Clear the color and depth buffer.
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

            //  Load the identity matrix.
            gl.LoadIdentity();

            gl.Rotate(angleY, 0.0f, 1.0f, 0.0f);
            gl.Rotate(angleX, 1.0f, 0.0f, 0.0f);

            gl.Scale(scale, scale, scale);
            cameraPosition.setAll(INITIAL_X, INITIAL_Y, INITIAL_Z);

            cameraPosition.rotateClockWiseY(angleY);
            cameraPosition.rotateClockWiseX(angleX);
            if (angleY > 360)
            {
                angleY = angleY - 360;
            }

            myOpenGL.drawnTriangleObjectList(gl, ascTriangleObjectList, cameraPosition);

            // this.selectedFunction.Text = "Rotação em Y = " + angleY;
            // Nudge the rotation.
            //angleY += 5.0f;
            // angleX += 5.0f;
            // scale += 0.1f;
        }

        private void leapMotionManipulation()
        {
            leapMotionController.update();

            if (leapMotionController.getHandsGesture().areTwoHandsOn())
            {
                if (leapMotionController.getHandsGesture().areTwoHandsFingerCount(1))
                {
                    enableRotateX();
                    this.selectedFunction.Text = "Rotação em X = " + angleX;
                }
                else if (leapMotionController.getHandsGesture().areTwoHandsFingerCount(2))
                {
                    enableRotateY();
                    this.selectedFunction.Text = "Rotação em Y = " + angleX;
                }
                else if (leapMotionController.getHandsGesture().areTwoHandsFingerCount(3))
                {
                    this.selectedFunction.Text = "Zoom = " + scale;
                    enableZoom();
                }
               
                if (leapMotionController.getHandsGesture().hasAnyHandClosed())
                {
                    
                    if (leapMotionController.getHandsGesture().getFingersCount() == 2)
                    {
                       // Console.Beep();
                        if (leapMotionController.getHandsGesture().isCircular())
                        {
                            exitWithSecurity();
                        }
                    }
                }
                if (leapMotionController.getHandsGesture().hasAnyHandOpen())
                {
                    if (leapMotionController.getHandsGesture().isCircular())
                    {
                        goToConfigScreen();
                    }

                }
            }
            else if (leapMotionController.getHandsGesture().isOneHandOn())
            {
                if (leapMotionController.getHandsGesture().isOneHandClosed())
                {
                    if (rotateXEnabled)
                    {
                        leapMotionRotateAngleX();
                    }
                    else if (rotateYEnabled)
                    {
                        leapMotionRotateAngleY();
                    }
                    else if (zoomEnabled)
                    {
                        leapMotionZoom();
                    }
                }
            }
        }

        private void enableZoom()
        {
            rotateXEnabled = false;
            rotateYEnabled = false;
            zoomEnabled = true;
        }

        private void enableRotateY()
        {
            rotateXEnabled = false;
            rotateYEnabled = true;
            zoomEnabled = false;
        }

        private void enableRotateX()
        {
            rotateXEnabled = true;
            rotateYEnabled = false;
            zoomEnabled = false;
        }

        private void leapMotionRotateAngleY()
        {
            if (leapMotionController.getHandXOscilationGesture().getMovmentStatus() == MovimentStatus.DECREASING)
            {
                angleY += 5.0f;
            }
            else if (leapMotionController.getHandXOscilationGesture().getMovmentStatus() == MovimentStatus.INCREASING)
            {
                angleY -= 5.0f;
            }
        }

        private void leapMotionRotateAngleX()
        {
            if (leapMotionController.getHandYOscilationGesture().getMovmentStatus() == MovimentStatus.DECREASING)
            {
                angleX -= 5.0f;
            }
            else if (leapMotionController.getHandYOscilationGesture().getMovmentStatus() == MovimentStatus.INCREASING)
            {
                angleX += 5.0f;
            }
        }

        private void leapMotionZoom()
        {
            if (leapMotionController.getHandZOscilationGesture().getMovmentStatus() == MovimentStatus.DECREASING)
            {
                scale -= 0.05f;
            }
            else if (leapMotionController.getHandZOscilationGesture().getMovmentStatus() == MovimentStatus.INCREASING)
            {
                scale += 0.05f;
            }
        }

        private void openGLControl_OpenGLInitialized(object sender, OpenGLEventArgs args)
        {
            OpenGL gl = openGLControl.OpenGL;
            myOpenGL.initialized(gl);
        }

        private void openGLControl_Resized(object sender, OpenGLEventArgs args)
        {
            OpenGL gl = openGLControl.OpenGL;
            myOpenGL.resized(gl, cameraPosition, Width, Height);
        }
        /*
        private void openGLControl_OpenGLDrawAxis(object sender, OpenGLEventArgs args)
        {
            OpenGL gl = openGLControlAxis.OpenGL;

            //  Clear the color and depth buffer.
            gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);

            //  Load the identity matrix.
            gl.LoadIdentity();

            gl.Rotate(angleY, 0.0f, 1.0f, 0.0f);
            gl.Rotate(angleX, 1.0f, 0.0f, 0.0f);

            gl.Scale(scale, scale, scale);
            cameraPosition.setAll(INITIAL_X, INITIAL_Y, INITIAL_Z);

            cameraPosition.rotateClockWiseY(angleY);
            cameraPosition.rotateClockWiseX(angleX);
            if (angleY > 360)
            {
                angleY = angleY - 360;
            }

            myOpenGL.drawnTriangleObjectList(gl, ascTriangleObjectList, cameraPosition);

            this.selectedFunction.Text = "Rotação em Y = " + angleY;

        }
        private void openGLControl_OpenGLInitializedAxis(object sender, OpenGLEventArgs args)
        {
            OpenGL gl = openGLControlAxis.OpenGL;
            myOpenGL.initialized(gl);
        }
        private void openGLControl_ResizedAxis(object sender, OpenGLEventArgs args)
        {
            OpenGL gl = openGLControlAxis.OpenGL;
            myOpenGL.resized(gl, cameraPosition, Width, Height);
        }
        */




    }
}
